﻿/////////////////////////////////////////////////////////////////////////
//                                                                     //
//    MTGProject - A Magic The Gathering Networked Environment         //
//                                                                     //
//        Copyright (c) 2008 by The MTGProject Team (CSC230)           //
//                                                                     //
// This file is part of MTGProject.                                    //
//                                                                     //
// MTGProject is free software; you can redistribute it and/or         //
// modify it under the terms of the GNU Lesser General Public          //
// License as published by the Free Software Foundation; either        //
// version 3 of the License, or (at your option) any later version.    //
//                                                                     //
// This library is distributed in the hope that it will be useful,     //
// but WITHOUT ANY WARRANTY; without even the implied warranty of      //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       //
// GNU Lesser General Public License for more details.                 //
//                                                                     //
// You should have received a copy of the GNU Lesser General Public    //
// License along with this library; if not, write to the Free Software //
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA           //
// 02111-1307, USA, or contact the author(s):                          //
//                                                                     //
// Ruben Acuna <flyingfowlsoftware@earthlink.net>                      //
// Ryan Costello <fusioncobra@yahoo.com>                               //
//                                                                     //
/////////////////////////////////////////////////////////////////////////

//If you don't understand preprocessor defines, please stay far, far,
//away from this class. ~ RA

//#define MANUAL_MODE

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Diagnostics;

namespace MTGProject
{
    class PlayerControllerNetworked : PlayerController
    {
        #region variables

        private TcpClient playerClient;
        private StreamReader connectionReceiver;
        private StreamWriter connectionSender;

        private Player puppet;
        private bool isConnected = false;

        #endregion

        #region properties

        public override Player Puppet
        {
            get { return puppet; }
            set { puppet = value; }
        }

        public override bool IsConnected
        {
            get { return isConnected; }
            set { isConnected = value; }
        }

        #endregion

        #region constructor

        public PlayerControllerNetworked(TcpClient tcpCon)
        {
            playerClient = tcpCon;

            AcceptPlayer();
        }

        #endregion

        #region networking

        private void AcceptPlayer()
        {
            string playerName;
            string deckCard;
            int deckCount;

            //setup streams
            connectionReceiver = new System.IO.StreamReader(playerClient.GetStream());
            connectionSender = new System.IO.StreamWriter(playerClient.GetStream());

            isConnected = true;

            //get username
            playerName = nextTextPacket();
            Puppet = new Player(playerName);
            sendTextPacket(ServerPackets.CONNECTION_SUCCESSFUL);
            GameServer.AddUser(playerClient, playerName);

            sendTextPacket(ServerPackets.REQUEST_SETUP_DECKCOUNT);
            deckCount = int.Parse(nextTextPacket());

            for (int x = 0; x < deckCount; x++)
            {
                //sendTextPacket(ServerPackets.REQUEST_SETUP_CARDNAME);
                deckCard = nextTextPacket();
                Puppet.Deck.AddCard(CardIO.LoadXMLCard(deckCard + ".xml", true));
            }

            System.Console.WriteLine(playerName + " is using a " + deckCount + " card deck:");
            UtilDebug.printCards(Puppet.Deck);

            sendTextPacket("You have connected as" + playerName + " and are using a " + deckCount + " card deck.");
        }

        public void DisconnectPlayer()
        {
            GameServer.RemoveUser(playerClient);

            isConnected = false;

            connectionReceiver.Close();
            connectionSender.Close();

            System.Console.WriteLine(Puppet.Name + "'s controller disconnected. Press enter to stop server.");
            System.Console.ReadLine();

            //HACK: this is unsafe. really unsafe. it will also cause the other client to crash and burn.
            Process.GetCurrentProcess().Kill();
        }

        public string nextTextPacket()
        {
            string packet = null;

            try
            {
                packet = connectionReceiver.ReadLine();

                if (packet == null)
                    DisconnectPlayer();

                if (packet.Equals(ClientPackets.REQUEST_DISCONNECT))
                    DisconnectPlayer();

                if(puppet != null)
                    System.Console.WriteLine(puppet.Name + ">>" + packet);
            }
            catch
            {
                DisconnectPlayer();
            }

            return packet;
        }

        public void sendTextPacket(string packet)
        {
            System.Console.WriteLine(puppet.Name + "<<" + packet);
            connectionSender.WriteLine(packet);
            connectionSender.Flush();
        }

        #endregion

        #region synchronization methods

        public void SynchronizeHand()
        {
            sendTextPacket(ServerPackets.SEND_PLAYER_HAND_RESET);

            //sendTextPacket(ServerPackets.SEND_SYNC_CARDPOOL_SIZE);
            //sendTextPacket(Puppet.Hand.Cards.Count.ToString());

            foreach (ICard card in Puppet.Hand.Cards)
            {
                sendTextPacket(ServerPackets.SEND_PLAYER_HAND_CARD);
                sendTextPacket(card.Name);
            }
        }

        public void SynchronizeHealth()
        {
            sendTextPacket(ServerPackets.SEND_PLAYER_HEALTH);
            sendTextPacket(Puppet.Health.ToString());
            sendTextPacket(ServerPackets.SEND_ENEMY_HEALTH);
            sendTextPacket(Puppet.Enemy.Health.ToString());
        }

        public void SynchronizePlayerPermanents()
        {
            sendTextPacket(ServerPackets.SEND_PLAYER_PERMANENTS_RESET);

            //sendTextPacket(ServerPackets.SEND_SYNC_CARDPOOL_SIZE);
            //sendTextPacket(Puppet.Permanents.Cards.Count.ToString());

            foreach (IPermanent permanent in Puppet.Permanents.Cards)
            {
                sendTextPacket(ServerPackets.SEND_PLAYER_PERMANENTS_CARD);
                sendTextPacket(permanent.Name);
            }
        }

        public void SynchronizeEnemyPermanents()
        {
            sendTextPacket(ServerPackets.SEND_ENEMY_PERMANENTS_RESET);

            //sendTextPacket(ServerPackets.SEND_SYNC_CARDPOOL_SIZE);
            //sendTextPacket(Puppet.Enemy.Permanents.Cards.Count.ToString());

            foreach (IPermanent permanent in Puppet.Enemy.Permanents.Cards)
            {
                sendTextPacket(ServerPackets.SEND_ENEMY_PERMANENTS_CARD);
                sendTextPacket(permanent.Name);
            }
        }

        public void SynchronizeGraveyard()
        {
            sendTextPacket(ServerPackets.SEND_PLAYER_GRAVEYARD_RESET);

            //sendTextPacket(ServerPackets.SEND_SYNC_CARDPOOL_SIZE);
            //sendTextPacket(Puppet.Graveyard.Cards.Count.ToString());

            foreach (ICard card in Puppet.Graveyard.Cards)
            {
                sendTextPacket(ServerPackets.SEND_PLAYER_GRAVEYARD_CARD);
                sendTextPacket(card.Name);
            }

            //TODO: should we also send the enemy's graveyard?
        }

        #endregion

        #region interface methods

        public override void PhaseMain()
        {
            string input = "";
            string inputAction;
            string inputData;

            SynchronizeHand();
            SynchronizeHealth();
            SynchronizePlayerPermanents();
            SynchronizeEnemyPermanents();
            SynchronizeGraveyard();

            sendTextPacket(ServerPackets.EVENT_TURN_START);

            sendTextPacket(ServerPackets.EVENT_PHASE_MAIN_START);

            while (!(input.Equals("q")))
            {
                input = nextTextPacket();
                inputAction = input.Split(' ')[0];
                inputData = input.Substring(input.IndexOf(' ') + 1);

                System.Console.WriteLine(Puppet.Name + ": " + input);

                if (inputAction.Equals("play"))
                {
                    ICard card = Puppet.Hand.GetCardByName(inputData);

                    if (card != null)
                        Puppet.PlayCard(card); //TODO: method needs to return status of action.
                    else
                        sendTextPacket(ServerPackets.ERROR_CARD_INVALID);
                }
                else if (inputAction.Equals("tap"))
                {
                    IPermanent card = null;

                    foreach (IPermanent permanent in Puppet.Permanents.Cards)
                    {
                        if ((permanent.IsTapped == false) && permanent.Name.ToLower().Equals(inputData))
                        {
                            card = permanent;
                            break;
                        }
                    }

                    if (card != null)
                        Puppet.TapCard(card); //TODO: method needs to return status of action.
                    else
                        sendTextPacket(ServerPackets.ERROR_CRASHANDBURN); //card is not in play or is tapped.
                }
                else if (inputAction.Equals("aacount"))
                {
                    ICard card = Puppet.Permanents.GetCardByName(inputData);

                    if (card != null && card is CardCreature)
                    {
                        sendTextPacket(ServerPackets.SEND_ACTIVATEDABILITY_COUNT);
                        sendTextPacket(((CardCreature)card).ActivatedAbilities.ToString());
                    }
                    else
                        sendTextPacket("0");
                }
                else if(inputAction.Contains("useaa"))
                {
                    ICard card = Puppet.Permanents.GetCardByName(inputData);

                    ((CardCreature)card).ActivateAbility(int.Parse(inputAction.Substring(5)));
                }
                else
                {
                    sendTextPacket(ServerPackets.ERROR_MESSAGE);
                    sendTextPacket("Server could not parse packet: " + input);
                }
            }

            sendTextPacket(ServerPackets.EVENT_PHASE_MAIN_END);

            sendTextPacket(ServerPackets.EVENT_TURN_END);
        }

        public override void OnLose()
        {
#if MANUAL_MODE
            System.Console.WriteLine("You have lost the game.");
#else
            sendTextPacket(ServerPackets.EVENT_LOSE);
#endif
        }

        public override void OnWin()
        {
#if MANUAL_MODE
            System.Console.WriteLine("You have won the game.");
#else
            sendTextPacket(ServerPackets.EVENT_WIN);
#endif
        }

        public override ICard SelectCard(LinkedList<ICard> cards)
        {
            return SelectCard(cards, "");
        }

        public override ICard SelectCard(LinkedList<ICard> cards, string message)
        {
            if (message.Length > 0)
                Tell(message);

            string selectedCard = "";

            sendTextPacket(ServerPackets.SEND_GUI_CARDSELECTION_RESET);//probably not needed

            //sendTextPacket(ServerPackets.SEND_SYNC_CARDPOOL_SIZE);
            //sendTextPacket(cards.Count.ToString());

            foreach (ICard card in cards)
            {
                sendTextPacket(ServerPackets.SEND_GUI_CARDSELECTION_CARD);
                sendTextPacket(card.Name);
            }

            //get card and translate it back to an ICard.
            selectedCard = nextTextPacket();
            foreach (ICard card in cards)
            {
                if (selectedCard.ToLower().Equals(card.Name.ToLower()))
                {
                    sendTextPacket(ServerPackets.SEND_GUI_CARDSELECTION_RESET);
                    return card;
                }
            }

            sendTextPacket(ServerPackets.SEND_GUI_CARDSELECTION_RESET);

            return null;//we should never get here.
        }

        public override void Tell(string message)
        {
#if MANUAL_MODE
            System.Console.WriteLine(message);
#else
            sendTextPacket(ServerPackets.SEND_MESSAGE);
            sendTextPacket(message);
#endif
        }

        #endregion

        #region combat phase methods

        public override int PhaseDefense(LinkedList<CardCreature> unblockedAttackers)
        {
            int floatingDamage = 0; //KEYWORD: TRAMPLE - this is the damage done over a lands health.
            LinkedList<CardCreature> possibleBlockers = new LinkedList<CardCreature>();
            string input = "";

            SynchronizeEnemyPermanents();
            SynchronizePlayerPermanents();

#if MANUAL_MODE
            //display instructions
            System.Console.WriteLine("Start Defense phase: Type the name of a card to block " +
                                     "and with what creature is blocking it. \n" +
                                     "Example: 'block raging goblin with grizzly bears' \n" +
                                     "Press q when done with Defense phase.");
#else
            sendTextPacket(ServerPackets.EVENT_PHASE_DEFENSE_START);
#endif            
            //build list of possible blockers
            foreach (IPermanent permanent in Puppet.Permanents.Cards)
            {
                if (permanent is CardCreature)
                {
                    CardCreature creature = (CardCreature)permanent;
                    if (creature.IsTapped == false)
                        possibleBlockers.AddLast(creature);
                }
            }
            
#if MANUAL_MODE
            //display permanents
            System.Console.WriteLine("Available blockers:");
            foreach (CardCreature creature in possibleBlockers)
                System.Console.WriteLine("  " + creature.Name);
#endif
#if MANUAL_MODE
            //display attackers
            System.Console.WriteLine("Attacking creatures:");
#endif
            foreach (CardCreature creature in unblockedAttackers)
            {
#if MANUAL_MODE
                System.Console.WriteLine("  " + creature.Name);
#else
                sendTextPacket(ServerPackets.SEND_CARD_ENEMY_ATTACKER);
                sendTextPacket(creature.Name);
#endif
            }

            while (!(input.Equals("q")))
            {
#if MANUAL_MODE
                input = System.Console.ReadLine().ToLower();
#else
                input = nextTextPacket().ToLower();
#endif
                
                if (input.Contains("block") && input.Contains("with"))
                {
                    string attacker = input.Substring(input.IndexOf(' ') + 1);
                    attacker = attacker.Substring(0, attacker.IndexOf(" with"));
                    string blocker = input.Substring(input.IndexOf("with ") + 5);

                    CardCreature attackingCard = null;
                    CardCreature blockingCard = null;

                    foreach (CardCreature card in unblockedAttackers)
                        if (card.Name.ToLower().Equals(attacker))
                            attackingCard = card;

                    foreach (CardCreature card in possibleBlockers)
                        if (card.Name.ToLower().Equals(blocker))
                            blockingCard = card;

                    if (attackingCard == null)
                    {
#if MANUAL_MODE
                        Console.WriteLine(attacker + " does not exist.");
#else
                        sendTextPacket(ServerPackets.SEND_MESSAGE);
                        sendTextPacket(attacker + " does not exist.");
#endif
                    }
                    else if (blockingCard == null)
                    {
#if MANUAL_MODE
                        Console.WriteLine(blocker + " does not exist.");
#else
                        sendTextPacket(ServerPackets.SEND_MESSAGE);
                        sendTextPacket(blocker + " does not exist.");
#endif
                    }
                    else if (UtilMagic.CanBlock(attackingCard, Puppet.Enemy, blockingCard, Puppet))
                    {
#if MANUAL_MODE
                        Console.WriteLine(blockingCard.Name + " blocks " + attackingCard.Name + ".");
#endif
                        blockingCard.OnBlocks(); //SCRIPTING HOOK

                        int blockingCardDamage = blockingCard.DealDamage();
                        int attackingCardDamage = attackingCard.DealDamage();

                        //Keyword: LIFELINK (probably complete)
                        if (blockingCard.HasKeyword(CreatureKeyword.LIFELINK))
                            Puppet.Heal(blockingCardDamage, blockingCard);
                        if (attackingCard.HasKeyword(CreatureKeyword.LIFELINK))
                            Puppet.Enemy.Heal(attackingCardDamage, attackingCard);

                        //Rule (hack): When a creature with Deathtouch does damage to another creature, that creature dies.
                        if (blockingCard.HasKeyword(CreatureKeyword.DEATHTOUCH))
                            blockingCardDamage = 999;

                        if(attackingCard.HasKeyword(CreatureKeyword.DEATHTOUCH))
                            attackingCardDamage = 999;

                        //Keyword: FIRST_STRIKE (complete)
                        //Rule: Creatures with First Strike deal damage first.
                        if(blockingCard.HasKeyword(CreatureKeyword.FIRST_STRIKE) || attackingCard.HasKeyword(CreatureKeyword.FIRST_STRIKE))
                        {
                            if (blockingCard.HasKeyword(CreatureKeyword.FIRST_STRIKE))
                            {
                                attackingCard.TakeDamage(blockingCardDamage, blockingCard);

                                if (attackingCard.Health > 0)
                                    blockingCard.TakeDamage(attackingCardDamage, attackingCard);
                            }
                            else
                            {
                                blockingCard.TakeDamage(attackingCardDamage, attackingCard);

                                if (blockingCard.Health > 0)
                                    attackingCard.TakeDamage(blockingCardDamage, blockingCard);
                            }
                        }
                        else //do damage normally
                        {
                            blockingCard.TakeDamage(attackingCardDamage, attackingCard);
                            attackingCard.TakeDamage(blockingCardDamage, blockingCard);
                        }

                        //KEYWORD: TRAMPLE
                        if (blockingCard.Health < 0 && !attackingCard.HasKeyword(CreatureKeyword.DEATHTOUCH))
                            floatingDamage += Math.Abs(blockingCard.Health);

                        possibleBlockers.Remove(blockingCard);
                        unblockedAttackers.Remove(attackingCard);
                    }
#if MANUAL_MODE
                    else
                        Console.WriteLine(blockingCard.Name + " cannot block " + attackingCard.Name + ".");
#endif

                }
#if MANUAL_MODE
                else
                    if (!input.Split(' ')[0].Equals("q"))
                        System.Console.WriteLine(input.Split(' ')[0] + " is not a valid action.");
#endif             
            }
#if MANUAL_MODE
            System.Console.WriteLine("End of Defending phase.");            
#else
            sendTextPacket(ServerPackets.EVENT_PHASE_DEFENSE_END);
#endif
            return floatingDamage;
        }

        public override void PhaseCombatStart()
        {
#if MANUAL_MODE
            System.Console.WriteLine("Start combat phase: Type the name of a card to attack or tap (e.g. 'attack raging goblin', or "
                       + "'tap mountain'. Press q when done with combat phase.");
#else
            sendTextPacket(ServerPackets.EVENT_PHASE_COMBAT_START);
#endif
        }

        public override CardCreature PhaseCombatRequestAttackingCreature()
        {
#if MANUAL_MODE
            string input = System.Console.ReadLine().ToLower();
#else
            string input = nextTextPacket().ToLower();
#endif

            while (!(input.Equals("q")))
            {
                if (input.Contains("attack"))
                {
                    string cardName = input.Substring(input.IndexOf(' ') + 1);

                    foreach (IPermanent permanent in Puppet.Permanents.Cards)
                    {
                        if (permanent is CardCreature && permanent.Name.ToLower().Equals(cardName.ToLower())
                            && !((CardCreature)permanent).HasKeyword(CreatureKeyword.DEFENDER)
                            //&& !((CardCreature)permanent).IsTapped
                            && !((CardCreature)permanent).IsSick)
                        {
                            return (CardCreature)permanent;
                        }
                    }
                }
#if MANUAL_MODE
                else
                    if (!input.Split(' ')[0].Equals("q"))
                        System.Console.WriteLine(input.Split(' ')[0] + " is not a valid action during the combat phase.");

                input = System.Console.ReadLine().ToLower();
#else
                input = nextTextPacket().ToLower();
# endif
            }

            return null;
        }

        public override void PhaseCombatEnd()
        {
#if MANUAL_MODE
             System.Console.WriteLine("End of Combat phase.");
#else
            sendTextPacket(ServerPackets.EVENT_PHASE_COMBAT_END);
#endif
        }

        #endregion
    }
}
